import os
import tkinter as tk
from PIL import ImageTk, Image, ImageSequence
from tkinter import filedialog, messagebox

# 全局变量
file_index = 0  # 全局文件指针
img_index = 0  # 全局图片指针
root_info = ()  # 全局窗口信息
file_path_m = ''  # 全局文件路径
img_style_list = ['bmp', 'jpg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'raw', 'wmf', 'webp', 'avif', 'apng']  # 全局图片格式列表
ok_file_list = []  # 全局文件列表
delete_stack = []  # 全局删除栈
exclude = ''  # 全局进程变量


def average(int_list):
    """
    求平均值
    :param int_list: 整数列表
    :return: 平均值
    """
    sum_s = 0
    length = len(int_list)
    for ele_n in int_list:
        sum_s += ele_n
    return sum_s / length


def put_photo():
    """
    在img标签展示图片，尽可能使图片占据全窗口,如果是gif则逐帧循环播放
    图片的获取:全局文件列表[全局文件指针]
    """
    def return_new_size_image(image_b):
        """
        调整图片大小
        :param image_b: 图片对象
        :return: 调整大小后的图片对象
        """
        image_size = image_b.size
        i_x, i_y = image_size
        if (i_x / i_y) > (l_x / l_y):
            mx = l_x
            my = int(mx * i_y / i_x)
        else:
            my = l_y
            mx = int(i_x * my / i_y)
        re_img = image_b.resize((mx, my))
        return re_img

    def updata_gif(index_m, durate):
        """
        更新gif图片
        :param index_m: 图片索引
        :param durate: 图片间隔时间
        """
        global exclude
        index_m = (index_m + 1) % len(photos)
        img_label.config(image=photos[index_m])
        exclude = root.after(int(durate), lambda v: updata_gif(index_m, durate), None)

    def get_frame_duration(image_b):
        """
        获取gif图片间隔时间
        :param image_b: 图片对象
        :return: 图片间隔时间
        """
        info_b = image_b.info
        if info_b != {} and 'duration' in info_b:
            return image_b.info['duration']
        return None

    global exclude
    if exclude != '':
        root.after_cancel(exclude)
    l_x = root_info[0] - 20  # 窗口宽度
    l_y = root_info[1] - 80  # 窗口高度
    global img_index
    img_index = 0
    if len(ok_file_list) > 0:  # 全局文件列表内没有文件
        file_path_n = ok_file_list[file_index]  # 选取文件
        title_label.config(text=file_path_n.split('/')[-1])  # 标题标签
        image = Image.open(file_path_n)  # 打开文件（转为图片对象）
        frams = ImageSequence.all_frames(image)  # 读取图片标签的所有帧
        if len(frams) > 1:  # 如果帧数大于1（gif文件）
            re_imgs = [return_new_size_image(x) for x in frams]  # 重新设置图片大小
            photos = [ImageTk.PhotoImage(y) for y in re_imgs]  # 转为图片对象
            info_list = [get_frame_duration(x) for x in frams]  # 获取每一帧持续时长
            if info_list:
                duration_n = average(info_list)
                if duration_n == 0:
                    duration_n = 100
                updata_gif(img_index, duration_n)  # 更新图片
            else:
                updata_gif(img_index, 100)
        if len(frams) == 1:  # 只有一帧，图片
            photo = ImageTk.PhotoImage(return_new_size_image(image))
            img_label.config(image=photo)
            img_label.image = photo  # 放置图片


def right_img():
    """
    修改全局文件指针，使指针右移
    """
    global file_index
    if img_label.config().get('image')[-1] != '':
        if file_index < len(ok_file_list) - 1:
            file_index += 1
            # print(file_index)
            put_photo()  # 放置（更新）图片


def left_img():
    """
    修改全局文件指针，使指针左移
    """
    global file_index

    if img_label.config().get('image')[-1] != '':
        if file_index > 0:
            file_index -= 1
            put_photo()  # 放置图片（更新）


def del_img():
    """
    删除图片，但不真正删除只是放到一个单独的文件夹
    删除的图片是：全局文件列表[全局文件指针]
    """
    global file_index
    if not os.path.exists(f'{file_path_m}/delete'):
        os.makedirs(f'{file_path_m}/delete')  # 如果没有delete文件夹则创建
    if len(ok_file_list) > 0:  # 判断选取的文件列表的长度
        if len(ok_file_list) == 1:
            delete_img_path = ok_file_list.pop(file_index)
            delete_stack.append(delete_img_path)
            file_name = delete_img_path.split('/')[-1]
            os.rename(f'{file_path_m}/{file_name}', f'{file_path_m}/delete/{file_name}')
            img_label.config(image='')
        elif len(ok_file_list) > 1 and file_index == len(ok_file_list) - 1:
            delete_img_path = ok_file_list.pop(file_index)
            delete_stack.append(delete_img_path)
            file_name = delete_img_path.split('/')[-1]
            os.rename(f'{file_path_m}/{file_name}', f'{file_path_m}/delete/{file_name}')
            file_index -= 1
            put_photo()
        else:
            delete_img_path = ok_file_list.pop(file_index)
            delete_stack.append(delete_img_path)
            file_name = delete_img_path.split('/')[-1]
            os.rename(f'{file_path_m}/{file_name}', f'{file_path_m}/delete/{file_name}')
            put_photo()


def recover_img():
    """
    恢复删除的图片
    """
    if len(delete_stack) > 0:  # 如果有删除的图片
        recover_img_path = delete_stack.pop(-1)
        file_name = recover_img_path.split('/')[-1]
        os.rename(f'{file_path_m}/delete/{file_name}', f'{file_path_m}/{file_name}')  # 将删除的图片重新放到全局文件列表
        ok_file_list.insert(file_index, recover_img_path)
        ok_file_list.sort()
        put_photo()


def configure_handle(event_n):
    """
    处理窗口事件
    """
    def ele_place():
        """
        重新放置元素
        """
        img_label.place(x=10, y=40, width=root_info_n[0] - 20, height=root_info_n[1] - 80)
        choose_button.place(x=10, y=10, width=115, height=20)
        path_text.place(x=125, y=10, width=root_info[0] - 135, height=20)
        recover_button.place(x=root_info[0] - 45, y=root_info[1] - 30, width=35, height=20)
        del_button.place(x=root_info[0] - 85, y=root_info[1] - 30, width=35, height=20)
        right_button.place(x=root_info[0] - 140, y=root_info[1] - 30, width=50, height=20)
        left_button.place(x=root_info[0] - 195, y=root_info[1] - 30, width=50, height=20)
        title_label.place(x=10, y=root_info[1] - 30, width=root_info[0] - 210, height=20)

    global root_info
    root_info_n = (root.winfo_width(), root.winfo_height())
    if root_info != root_info_n:
        root_info = root_info_n  # 更新视窗大小
        ele_place()  # 更新元素位置
        if img_label.config().get('image')[-1] != '':
            put_photo()  # 重新放置图片


def keypress_handle(event_n):
    """
    处理键盘事件
    """
    event_key = event_n.keysym.lower()
    if event_key == 'delete':
        del_img()
    if event_key == 'right':
        right_img()
    if event_key == 'left':
        left_img()
    if event_n.state == 4 and event_key == 'z':
        recover_img()


def choose_command():
    """
    利用tk提供的api选择文件夹，获取文件列表，更新全局变量
    """
    global ok_file_list
    global file_path_m
    global file_index
    ok_file_list = []
    file_index = 0
    file_path_a = filedialog.askdirectory()  # 选取文件夹
    if file_path_a != '':
        file_list = os.listdir(file_path_a)  # 获取所有文件
        if file_list:  # 如果选择的文件夹不为空
            for one_file in file_list:
                if os.path.isfile(f'{file_path_a}/{one_file}'):
                    if '.' not in one_file:  # 文件规范，例如'.jpg'
                        pass
                    else:
                        file_tail = one_file.split('.')[-1].lower()
                        if file_tail not in img_style_list:
                            pass
                        else:
                            ok_file_list.append(f'{file_path_a}/{one_file}')  # 只有符合文件规范的文件才会放到全局文件列表
        if not ok_file_list:
            messagebox.showerror('错误', '路径下无可打开文件')  # 全局文件列表为空
        else:
            path_text.config(state='normal')  #
            file_path_m = file_path_a  # 将选择的文件夹更新到全局变量
            path_text.delete('1.0', tk.END)
            path_text.insert(tk.END, file_path_a)
            path_text.config(state='disabled')
            ok_file_list.sort()  # 排序
            put_photo()


# 创建窗口
root = tk.Tk()
root.geometry('500x400+500+300')  # 放置窗口
root.minsize(230, 180)  # 设置窗口最小尺寸
root.title('图片查看器')  # 窗口标题设置
root.iconbitmap(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pic.ico'))  # 设置图标

#

img_label = tk.Label(root, bg='white')  # 图片标签，用于防放置图片
choose_button = tk.Button(root, text='选择图片路径：', font=('宋体', 12), bd=1, command=choose_command)  # 选择图片路径按钮
path_text = tk.Text(root, bg='white', font=('宋体', 11), state='disabled')  # 显示当前选择的文件夹路径，如果有否则为空

left_button = tk.Button(root, text='上一张', font=('宋体', 12), bd=1, command=left_img)  # 上一张按钮
right_button = tk.Button(root, text='下一张', font=('宋体', 12), bd=1, command=right_img)  # 下一张按钮
del_button = tk.Button(root, text='删除', font=('宋体', 12), bd=1, command=del_img)  # 删除按钮
recover_button = tk.Button(root, text='恢复', font=('宋体', 12), bd=1, command=recover_img)  # 恢复按钮
title_label = tk.Label(root, font=('宋体', 12), anchor='w')  # 用于显示图片名字

#

root.bind("<Configure>", configure_handle)  # 绑定事件
root.bind("<KeyPress>", keypress_handle)
root.mainloop()  # 启动主程序
